#include <string.h>
#include <stdlib.h>
#include "aos/kernel.h"
#include "lwip/def.h"
#include "lwip/err.h"
#include "aolisthead.h"
#include "memheap.h"
#include "devent.h"

#define MAX_EVENT_HASH		64
#define MAX_TASK			2
#define AoSnprintf _snprintf

typedef struct _EventTask{
	AoListHead			list;
	aos_task_t			task;
	AoListHead			events;
	aos_sem_t			sem;
}EventTask;

typedef struct _EventSubscribe{
	AoListHead			list;
	char*				name;
	device_event_cb_t	cb;
}EventSubscribe;

typedef struct _EventQueue{
	void*				name_heap;
	AoListHead			events;
	AoListHead*			byNameHash;
}EventQueue;
typedef struct _EventMgr{
	void*			home;
	aos_mutex_t		lock;
	AoListHead		subscribes; // the new orders
	AoListHead*		bynameHash[DeviceEventTypeMax];
	AoListHead		idleTask;
	AoListHead		runTask;
	AoListHead		events;
}EventMgr;

static EventMgr * mgr_e=NULL;

static void event_task(void * arg)
{
	EventTask * task;
	device_event_t * event;
	EventSubscribe *subscribe;

	task = (EventTask *)arg;
	AoListHeadDelInit(&task->list);
	aos_mutex_lock(&mgr_e->lock,-1);
	AoListHeadAdd(&task->list,&mgr_e->idleTask);
	aos_mutex_unlock(&mgr_e->lock);

	while(1)
	{
		aos_sem_wait(&task->sem,AOS_WAIT_FOREVER);
		aos_mutex_lock(&mgr_e->lock,-1);
		
		if(AoListHeadEmpty(&mgr_e->events))
		{
			aos_mutex_unlock(&mgr_e->lock);
			continue;
		}
//		AoListHeadDelInit(&task->list);
		event = (device_event_t*)mgr_e->events.prev;
		AoListHeadDelInit(&event->list);
		aos_mutex_unlock(&mgr_e->lock);
		subscribe = event->subscribe;
		if(subscribe&&subscribe->cb)
		{
			subscribe->cb(&event->data[0]);
		}

		device_event_destruct(event);
	}
}
int device_event_init()
{
	int i,j;
	void * home;
	EventTask * task;

	if(!mgr_e)
	{
		memHeapCreate(&home,0);
		mgr_e = (EventMgr*)memHeapAlloc(&home,sizeof(EventMgr));
		if(!mgr_e)
		{
			return ERR_MEM;
		}
		mgr_e->home = home;
		aos_mutex_new(&mgr_e->lock);
		AO_LIST_HEAD_INIT(&mgr_e->idleTask);
		AO_LIST_HEAD_INIT(&mgr_e->runTask);
		AO_LIST_HEAD_INIT(&mgr_e->subscribes);
		AO_LIST_HEAD_INIT(&mgr_e->events);

		for(j=0;j<DeviceEventTypeMax;j++)
		{
			mgr_e->bynameHash[j] = (AoListHead*)memHeapAlloc(&home,sizeof(AoListHead)*MAX_EVENT_HASH);
			if(!mgr_e->bynameHash[j])
			{
				return ERR_MEM;
			}
			for(i=0;i<MAX_EVENT_HASH;i++)
			{
				AO_LIST_HEAD_INIT(&mgr_e->bynameHash[j][i]);
			}
		}
		for(i=0;i<MAX_TASK;i++)
		{
			task = (EventTask*)memHeapAlloc(&home,sizeof(EventTask));
			if(!task)
			{
				return ERR_MEM;
			}
			AO_LIST_HEAD_INIT(&task->events);
			AO_LIST_HEAD_INIT(&task->list);
			aos_sem_new(&task->sem,1);
			aos_task_new_ext(&task->task,"event",event_task,task, 50 * 1024, 11);
		}
	}
	return ERR_OK;
}
int CmEventEnd()
{
	if(mgr_e)
	{
		aos_mutex_free(&mgr_e->lock);
	}
	return ERR_OK;
}

device_event_t * device_event_construct(char * name,device_event_type_t type)
{
	device_event_t * event;
	void * pTypeMemHeap;
	

	memHeapCreate (&pTypeMemHeap,0);
	event = (device_event_t*)memHeapAllocZ(&pTypeMemHeap,sizeof(device_event_t)+strlen(name)+1);
	if(!event)
	{
		return NULL;
	}
	
	event->pTypeMemHeap = pTypeMemHeap;

	

//	event->time = AoClockGet(NULL);
	event->type = type;
	event->name = (char*)event+sizeof(device_event_t);
	strcpy(event->name,name);
	event->state = DeviceEventStateInit;

	AO_LIST_HEAD_INIT(&event->list);
//	AO_LIST_HEAD_INIT(&event->link);
		
	return event;
}


int device_event_destruct(device_event_t * event)
{
	aos_mutex_lock(&mgr_e->lock,-1);
//	AoListHeadDelInit(&event->link);
	AoListHeadDelInit(&event->list);
	aos_mutex_unlock(&mgr_e->lock);
	memHeapRelease(&event->pTypeMemHeap);
	return ERR_OK;
}

void * device_event_alloc(device_event_t * evt,int size)
{
	if(!evt)
		return NULL;
	return memHeapAlloc(&evt->pTypeMemHeap,size);
}

int device_event_notify(device_event_t * event)
{
	EventTask * task;
	AoListHead *head,*__i;
	EventSubscribe *subscribe;
	u32_t hash=0;
	char * p=event->name;
	while(*p)
	{
		hash = hash << 1 ^ (*p);
		p++;
	}
	hash&=(MAX_EVENT_HASH-1);
	aos_mutex_lock(&mgr_e->lock,-1);
	head = &mgr_e->bynameHash[event->type][hash];
	__i = head->next;
	while(head!=__i)
	{
		subscribe = (EventSubscribe *)__i;
		if(0==strcmp(subscribe->name,event->name))
		{
			event->subscribe = subscribe;
			break;
		}
		__i = __i->next;
	}
	if(!event->subscribe)
	{
		aos_mutex_unlock(&mgr_e->lock);
		device_event_destruct(event);
		return ERR_OK;
	}
	AoListHeadAdd(&event->list,&mgr_e->events);
	if(AoListHeadEmpty(&mgr_e->idleTask))
	{
		aos_mutex_unlock(&mgr_e->lock);
		device_event_destruct(event);
		return ERR_OK;
	}
//	AoListHeadDelInit(&task->list);
	task = (EventTask*)mgr_e->idleTask.prev;
	aos_mutex_unlock(&mgr_e->lock);
	aos_sem_signal(&task->sem);
	return ERR_OK;
}



int device_event_subscribe(device_event_type_t type,char * name,device_event_cb_t cb)
{
	EventSubscribe *subscribe;
	u32_t hash=0;
	char * p=name;
	while(*p)
	{
		hash = hash << 1 ^ (*p);
		p++;
	}
	hash&=(MAX_EVENT_HASH-1);

	aos_mutex_lock(&mgr_e->lock,-1);
	subscribe = (EventSubscribe*)memHeapAlloc(&mgr_e->home,sizeof(EventSubscribe)+strlen(name)+1);
	if(!subscribe)
	{
		aos_mutex_unlock(&mgr_e->lock);
		return ERR_OK;
	}
	subscribe->name = (char*)subscribe+sizeof(EventSubscribe);
	strcpy(subscribe->name,name);
	subscribe->cb = cb;
	AO_LIST_HEAD_INIT(&subscribe->list);
	AoListHeadAdd(&subscribe->list,&mgr_e->bynameHash[type][hash]);
	
	aos_mutex_unlock(&mgr_e->lock);
	return 0;
}


